"use client"; /** * /w/:workspace/inbox — T-164: the task inbox. * * Tabs (all server-filtered, `scope` = my assignments unless "All"): * Mine scope=inbox (assigned to me, any status) * Due scope=inbox & dueBefore=+7d * Overdue scope=inbox & overdue=true (due[1] { switch (tab) { case "due": return { scope: "inbox", limit: 100, dueBefore: new Date(Date.now() + 7 * 86_400_000).toISOString() }; case "overdue": return { scope: "inbox", limit: 100, overdue: true }; case "done": return { scope: "inbox", limit: 100, status: "done" }; case "all": return { scope: "all", limit: 100 }; default: return { scope: "inbox", limit: 100 }; } } const STATUS_META: Record = { todo: { label: "To do", Icon: Circle, cls: "text-slate-500" }, in_progress: { label: "In progress", Icon: CircleDashed, cls: "text-amber-600" }, done: { label: "Done", Icon: CheckSquare, cls: "text-emerald-600" }, cancelled: { label: "Cancelled", Icon: XCircle, cls: "text-rose-500" } }; const NEXT: Record = { todo: "in_progress", in_progress: "done", done: null, cancelled: "todo" }; export default function InboxPage() { const params = useParams<{ workspace: string }>(); const slugOrId = params?.workspace ?? ""; const { data: workspace } = useWorkspace(slugOrId); const wid = workspace?.id ?? slugOrId; const qc = useQueryClient(); const [tab, setTab] = useState("mine"); const [creating, setCreating] = useState(false); const [newTitle, setNewTitle] = useState(""); const list = useQuery({ queryKey: ["tasks", wid, tab], queryFn: () => taskApi.list(wid, tabParams(tab)), enabled: !!workspace?.id, retry: false }); const invalidate = () => { for (const t of TABS) qc.invalidateQueries({ queryKey: ["tasks", wid, t.id] }); }; const create = useMutation({ mutationFn: (title: string) => taskApi.create(wid, { title }), onSuccess: () => { setCreating(false); setNewTitle(""); invalidate(); } }); const setStatus = useMutation({ mutationFn: (t: Task) => taskApi.update(wid, t.id, { status: NEXT[t.status]! }), onSuccess: invalidate }); const remove = useMutation({ mutationFn: (id: string) => taskApi.remove(wid, id), onSuccess: invalidate }); const items = list.data?.data ?? []; return (

Inbox

{creating ? (
{ e.preventDefault(); if (newTitle.trim()) create.mutate(newTitle.trim()); }} className="flex items-center gap-2" > setNewTitle(e.target.value)} placeholder="Task title" className="w-56 rounded-md border border-slate-300 px-2 py-1 text-sm" />
) : ( )}
{list.isLoading ? (
) : items.length === 0 ? (

No tasks here.

) : (
    {items.map((t) => { const meta = STATUS_META[t.status]; const overdue = t.dueAt && new Date(t.dueAt) < new Date() && t.status !== "done" && t.status !== "cancelled"; return (
  • {NEXT[t.status] ? ( ) : ( )}
    {t.pageTitle ?? t.title} {t.dueAt ? ( {overdue ? "Overdue — " : "Due "} {formatRelativeTime(t.dueAt)} ) : null}
  • ); })}
)}
); }